#include "StdAfx.h"
#include "SlnIconHandler.h"
#include "Unicode.h"
#include "diagnostics.h"
#include <fstream>
#include <algorithm>
#include <sstream>
#include <statreg.h>

using namespace ATL;
using namespace std;



//[importidl("ShObjIdl.idl")];//There is another attribute called importlib which could have been used too if you could locate
//a tlb/dll containing the definition of IExtractIcon

[
	coclass,
	//implements(interfaces={IPersistFile}),
	threading(apartment),
	vi_progid("SharpDevelop.SlnIconHandler"),
	progid("SharpDevelop.SlnIconHandler.1"),
	version(1.0),
	uuid("23020D1D-7050-4694-B6C7-DA5563C49955"),
	helpstring("The SharpDevelop SLN IconHandler Class")
]
class ATL_NO_VTABLE SlnIconHandler : 
	public IExtractIcon,
	public IPersistFile,
	public ISharpLauncher
{
public:
	SlnIconHandler()
		: _isSharpDevelopSolution(false)
	{
		dbgPrint(_T("SlnIconHandler::SlnIconHandler()\n"));
	}


	// *** IExtractIcon methods ***
	HRESULT STDMETHODCALLTYPE GetIconLocation(UINT   uFlags,
		LPWSTR pszIconFile,
		UINT   cchMax,
		int   * piIndex,
		UINT  * pwFlags)
	{
		dbgPrint(_T("SlnIconHandler::GetIconLocation()\n"));
		////A handle to the required icon will be provided in the call to the extract method
		//*pwFlags = GIL_NOTFILENAME;

		if(_isSharpDevelopSolution)
		{
			USES_CONVERSION;
			wcsncpy(pszIconFile, (LPOLESTR)T2W((LPTSTR)_pathToSharpDevelop.c_str()), cchMax);
			*piIndex = 0;
		}
		else
		{
			//TODO: Call previous IconHandlers GetIconLocation
			if(_prevHandler)
				return _prevHandler->GetIconLocation(uFlags, pszIconFile, cchMax, piIndex, pwFlags);
		}
		return S_OK;
	}

	HRESULT STDMETHODCALLTYPE Extract(LPCWSTR pszFile,
		UINT    nIconIndex,
		HICON   *phiconLarge,
		HICON   *phiconSmall,
		UINT    nIconSize)
 	{
		dbgPrint(_T("SlnIconHandler::Extract()\n"));
		//*phiconSmall = LoadIcon(NULL, IDI_EXCLAMATION);
		//*phiconLarge = LoadIcon(NULL, IDI_EXCLAMATION);
		//return S_OK;

		if(_isSharpDevelopSolution)
		{
			return S_FALSE;
		}
		else
		{
			//TODO: Call previous IconHandlers Extract method
			if(_prevHandler)
				return _prevHandler->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
			return S_OK;
		}
	}

	//IPersist methods
	HRESULT STDMETHODCALLTYPE GetClassID( CLSID *pClassID)
	{
		dbgPrint(_T("SlnIconHandler::GetClassID()\n"));
		*pClassID  = __uuidof(SlnIconHandler);
		return S_OK;
	}

	///IPersistFile methods
	HRESULT STDMETHODCALLTYPE IsDirty( void)
	{
		dbgPrint(_T("SlnIconHandler::IsDirty()\n"));
		return E_NOTIMPL;
	}

	HRESULT STDMETHODCALLTYPE Load( 
		/* [in] */ LPCOLESTR pszFileName,
		/* [in] */ DWORD dwMode)
	{
		dbgPrint(_T("SlnIconHandler::Load(%s, %d)\n"), pszFileName, dwMode);
		USES_CONVERSION;
		_slnFileName = W2T((LPWSTR)pszFileName);
		parseSolutionFile();
		if(!_isSharpDevelopSolution)
		{
			instantiatePrevHandler();
			if(_prevHandler)
			{
				CComPtr<IPersistFile> p;
				 _prevHandler.QueryInterface(&p);
				return p->Load(pszFileName, dwMode);
			}
		}
		return S_OK;
	}

	HRESULT STDMETHODCALLTYPE Save( /* [unique][in] */  LPCOLESTR pszFileName,
		/* [in] */ BOOL fRemember)
	{
		dbgPrint(_T("SlnIconHandler::Save()\n"));
		return E_NOTIMPL;
	}

	HRESULT STDMETHODCALLTYPE SaveCompleted( /* [unique][in] */ LPCOLESTR pszFileName)
	{
		dbgPrint(_T("SlnIconHandler::SaveCompleted()\n"));
		return E_NOTIMPL;
	}

	HRESULT STDMETHODCALLTYPE GetCurFile( /* [out] */ LPOLESTR *ppszFileName)
	{
		dbgPrint(_T("SlnIconHandler::GetCurFile()\n"));
		return E_NOTIMPL;
	}

	HRESULT STDMETHODCALLTYPE Launch(/*[in]*/ BSTR filePath)
	{
		dbgPrint(_T("SlnIconHandler::Launch(\"%s\")\n"), (TCHAR*)_bstr_t(filePath));
		_slnFileName = _bstr_t(filePath);
		parseSolutionFile();

		if(_isSharpDevelopSolution)
			return launchWith((TCHAR*)_bstr_t(filePath), _T("SharpDevelop.sln"));

		HRESULT retVal = S_OK;
		try
		{
			_bstr_t prevLauncher = _shell->RegRead(APP_REG_KEY _T("\\prevLauncher\\"));
            launchWith((TCHAR*)_bstr_t(filePath), (TCHAR*)prevLauncher);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not register icon handler. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
			retVal = E_FAIL;
		}
		return retVal;
	}

	HRESULT STDMETHODCALLTYPE IDELocated(/*[in]*/ BSTR ideName,
											/*[out, retval]*/ BOOL *retVal)
	{
		return E_NOTIMPL;
	}

	DECLARE_PROTECT_FINAL_CONSTRUCT()
	HRESULT FinalConstruct()
	{
		dbgPrint(_T("SlnIconHandler::FinalConstruct()\n"));
		_shell.CreateInstance(__uuidof(WshShell));
		locateSharpDevelop();
		return S_OK;
	}

	void FinalRelease() 
	{
		dbgPrint(_T("SlnIconHandler::FinalRelease()\n"));
	}
private:

	HRESULT launchWith(_tstring filePath, _tstring strClass)
	{
		dbgPrint(_T("SlnIconHandler::launchWith(, %s)\n"), strClass.c_str());
		SHELLEXECUTEINFO sei = { 0 };
		sei.cbSize = sizeof( sei );
		sei.fMask = SEE_MASK_CLASSNAME | SEE_MASK_FLAG_DDEWAIT;
		sei.lpVerb = _T("open");
		sei.nShow = SW_SHOWNORMAL;
		sei.lpClass = strClass.c_str();
		sei.lpFile = filePath.c_str();

		if(!ShellExecuteEx(&sei))
			return E_FAIL;

		return S_OK;
	}

	_tstring getUnquotedString(_tstring slnFileName)
	{
		int startIndex = 0;
		int lastIndex = slnFileName.length() - 1;
		if(slnFileName[startIndex] == _T('"'))
			startIndex++;
			
		if(slnFileName[lastIndex] == _T('"'))
			lastIndex--;
			
		return slnFileName.substr(startIndex, lastIndex-startIndex+1);
	}
	
	void parseSolutionFile()
	{
		dbgPrint(_T("SlnIconHandler::parseSolutionFile()\n"));
		
		_tstring slnFileName = getUnquotedString(_slnFileName);
		
		_tifstream slnFile(slnFileName.c_str());
		_tstring line;
		while(slnFile)
		{
			getline(slnFile, line);
			transform(line.begin(), line.end(), line.begin(), ::tolower);
			_tstringstream ss;
			_tstring token;
			ss<<line;
			ss>>token;
			if( token == _T("#"))
			{
				ss>>token;
				if(token == _T("sharpdevelop"))
				{
					_isSharpDevelopSolution = true;
					dbgPrint(_T("_isSharpDevelopSolution = true;\n"));
					ss>>_sharpDevVersion;
				}
				else if(token == _T("visual"))
				{
					ss>>token;
					if(token == _T("studio"))
					{
						ss>>_vsVersion;
					}
				}
			}
		}
	}

	void locateSharpDevelop()
	{
		//Query registry key HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\SharpDevelop.exe
		//to find the path to sharpdevelop.exe

		try
		{
			_variant_t retVal = _shell->RegRead(_T("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\SharpDevelop.exe\\"));
			_pathToSharpDevelop = _bstr_t(retVal.bstrVal);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not read sharpdevelop.exe app path from the registry. %s")
					, err.Error()
					, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}
	
	void instantiatePrevHandler()
	{

		if(_prevHandler)
			return;

		try
		{
			_bstr_t prevLauncher = _shell->RegRead(APP_REG_KEY _T("\\prevLauncher\\"));
			
				
			_bstr_t key = _T("HKCR\\");

			key += prevLauncher;
			key += _T("\\ShellEx");
			key += _T("\\IconHandler\\");

			_bstr_t strClsid = _bstr_t(_shell->RegRead(key));

			CLSID clsid;
			CLSIDFromString((LPOLESTR)strClsid, &clsid);
			
			_prevHandler.CoCreateInstance(clsid);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not retrieve previous application launcher's information. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}
private:
	_tstring				_slnFileName;
	bool					_isSharpDevelopSolution;
	_tstring				_sharpDevVersion;
	_tstring				_vsVersion;
	_tstring				_pathToSharpDevelop;
	CComPtr<IExtractIcon>	_prevHandler;
	IWshShellPtr			_shell;
};